package com.hzit.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonData;
import com.hzit.search.service.ItemSearchService;
import com.hzit.search.vo.ItemVo;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.system.api.domain.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类名：
 * 作者：WF
 * 功能：
 */
@Service
public class ItemSearchServiceImpl implements ItemSearchService {
	@Autowired
	private ElasticsearchClient client;
	@Autowired
	private RedisService redisService;
	/**
	 * 搜索商品
	 * @param vo
	 * @return
	 */
	@Override
	public Map<String, Object> search(ItemVo vo) throws IOException {
		Map<String, Object> resultMap = new HashMap<>();
		SearchRequest request = null;
		// 1. 定义查询列表
		List<Query> queryList = new ArrayList<>();
		// 封装所有的排序查询
		List<SortOptions> sortList = new ArrayList<>();

		// 2. 进行各种条件过滤查询
		// 2.1 分类查询（前端点击某个分类）
		if(StringUtils.isNotBlank(vo.getCategory())){
			// 2.2 得到分类的查询对象
			Query categoryQuery = TermQuery.of(tq -> tq.field("category").value(vo.getCategory()))._toQuery();
			// 2.3 将其放到查询集合中
			queryList.add(categoryQuery);
		}
		// 3. 进行品牌查询
		if(StringUtils.isNotBlank(vo.getBrand())){
			Query brandQuery = TermQuery.of(tq -> tq.field("brand").value(vo.getBrand()))._toQuery();
			queryList.add(brandQuery);
		}
		// 4. 进行规格查询
		if(vo.getSpec() != null && vo.getSpec().size() > 0){
			List<String> spec = vo.getSpec();
			for (String s : spec) {
				String[] split = s.split(":");      // split[0]: 规格名字 split[1]: 规格选项的值
				Query specQuery = TermQuery.of(tq -> tq.field("specMap." + split[0] + ".keyword").value(split[1]))._toQuery();
				queryList.add(specQuery);
			}

		}
		// 5. 价格区间查询
		if(StringUtils.isNotBlank(vo.getPrice())){
			String price = vo.getPrice();
			String[] split = price.split("-");      // split[0]: 最小价格 split[1]: 最大值
			Query minRangeQuery = RangeQuery.of(rq -> rq.field("price").gte(JsonData.of(split[0])))._toQuery();
			queryList.add(minRangeQuery);
			if(!split[1].equals("*")){
				Query maxRangeQuery = RangeQuery.of(rq -> rq.field("price").lte(JsonData.of(split[1])))._toQuery();
				queryList.add(maxRangeQuery);
			}
		}
		// 6. 开始排序查询（将所有要排序的请求封装在list中）
		if(StringUtils.isNotBlank(vo.getSort())){
			String sort = vo.getSort();
			String[] split = sort.split(":");       // split[0]: 代表排序字段 split[1]: 排序的值
			if(split[1].equals("asc")) {
				SortOptions ascSort = SortOptions.of(so -> so.field(f -> f.field(split[0]).order(SortOrder.Asc)));
				sortList.add(ascSort);
			}else if(split[1].equals("desc")){
				SortOptions descSort = SortOptions.of(so -> so.field(f -> f.field(split[0]).order(SortOrder.Desc)));
				sortList.add(descSort);
			}

		}
		int page = vo.getPage();            // 当前页
		int pageSize = vo.getPageSize();    // 每页大小
		if(StringUtils.isNotBlank(vo.getKeywords())){
			// 2. 添加多字段匹配查询
			Query multiMatchQuery = MultiMatchQuery.of(mmq -> mmq.fields("brand", "category", "title").query(vo.getKeywords()))._toQuery();
			queryList.add(multiMatchQuery);


			request = SearchRequest.of(ss -> ss.index("item")
					.query(q -> q.bool( b -> b.filter(queryList))) // bool -> must(and) should(or) filter
							 .highlight(hl -> hl.fields("title",                     // 高亮字段
									            hf -> hf.preTags("<span style='color:red'>")   // 高亮前缀字段
											            .postTags("</span>")                   // 高亮字段的后缀
							 ))
							.sort(sortList)                 // 排序字段
							.from((page - 1) * pageSize)    // 从哪行记录开始
							.size(pageSize)                 // 每页条数
							.aggregations("agg_category",ag -> ag.terms(tr -> tr.field("category"))));
		}else{
			request = SearchRequest.of(sr -> sr.index("item")
											   .query(q -> q.matchAll(qm -> qm))        // 查询所有商品
												.aggregations("agg_category",ag -> ag.terms(tr -> tr.field("category"))
												));
		}
		// 分类列表
		List<String> categoryList = new ArrayList<>();
		// 查询出的sku商品列表
		List<Item> itemList = new ArrayList<>();
		// 开始查询
		SearchResponse<Item> searchResponse = client.search(request, Item.class);


		HitsMetadata<Item> hits = searchResponse.hits();
		TotalHits totalHits = hits.total();
		long total = totalHits.value();                             // 得到总记录
		long totalpage = (long)Math.ceil(total / (1.0 * pageSize));

		for (Hit<Item> hit : hits.hits()) {
			Item item = hit.source();              // 未经过高亮前的数据
			Map<String, List<String>> highlight = hit.highlight();  // 高亮数据
			List<String> title = highlight.get("title");
			if(!CollectionUtils.isEmpty(title)){
				String s = title.get(0);          // 相当于：<span style="color:red">苹果</span>
				item.setTitle(s);                 // 重新设置标题字段，是有高亮显示数据的字段
			}
			itemList.add(item);                   // 将sku商品添加到商品列表中
		}

		// 获取聚合结果
		Map<String, Aggregate> aggregations = searchResponse.aggregations();
		Aggregate aggCategory = aggregations.get("agg_category");
		List<StringTermsBucket> array = aggCategory.sterms().buckets().array();
		for (StringTermsBucket bucket : array) {
			String key = bucket.key();
			// 将聚合结果放到集合中
			categoryList.add(key);
		}

		// 在结果map中存放分类列表数据
		resultMap.put("categoryList",categoryList);
		// 将sku商品列表添加到结果集合中
		resultMap.put("itemList",itemList);

		// 定义分类名字，如果前端没有传入，就使用聚合出来的第一个名字作为分类的名字
		String category = "";
		if(StringUtils.isNotBlank(vo.getCategory())){
			category = vo.getCategory();
		}else{
			category = categoryList.get(0);
		}
		// 根据分类查询出品牌列表及规格列表
		Map brandAndSpecList = findBrandAndSpecListByCategory(category);
		// 将上面的map放到结果map中s
		resultMap.putAll(brandAndSpecList);
		resultMap.put("totalpage",totalpage);   // 总页数
		resultMap.put("page",vo.getPage());     // 当前页
		return resultMap;
	}

	private Map findBrandAndSpecListByCategory(String category) {
		Map brandAndSpecMap = new HashMap();
		if(StringUtils.isNotBlank(category)) {
			// 1. 根据大key与小key(分类名字)得到模板id
			String typeId = (String) redisService.getCacheMap("itemCatList").get(category);
			// 2. 根据模板id，查询出品牌列表及规格列表
			List<Map> brandList = (List<Map>) redisService.getCacheMap("brandList").get(typeId);
			List<Map> specList = (List<Map>) redisService.getCacheMap("specList").get(typeId);
			brandAndSpecMap.put("specList",specList);
			brandAndSpecMap.put("brandList",brandList);
		}
		return brandAndSpecMap;
	}
}
